home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-05 / fp2_x102.zip / FP2_XLIB.PRG < prev   
Text File  |  1991-08-16  |  43KB  |  1,558 lines

  1. *
  2. *
  3. *  FP2_XLIB version 1.02        Release date 08-17-1991
  4. *                               Author       Edward M. Rauh
  5. *                                            Gamboge International
  6. *                                            300 Long Beach Blvd.
  7. *                                            Stratford, CT  06497
  8. *                                            FAX 203/386-9333
  9. *
  10. * Changes in 1.02:
  11. *
  12. *  new UDF - CHG_DIR(newpath,set_2_dflt)
  13. *
  14. *               changes directories on non-default drives, optionally
  15. *               sets new path to default.  Does not use RUN/!
  16. *
  17. *  new UDF - ISBN_CKD(booknumb)
  18. *
  19. *               cacluates the check digit needed to convert a 9-digit
  20. *               book number to an ISBN
  21. *
  22. *  Reductions in use of & operator to improve speed
  23. *
  24. *  No, the job server is not being released yet for FoxPro2 - you can
  25. *  still use the one I released for Foxbase+ in LAN_PROC;  it is likely
  26. *  that the FoxPro 2 specific version will be a commercial product in
  27. *  conjunction with a Novell/NetBIOS function library written with the
  28. *  C API.
  29. *
  30.  
  31.  
  32. procedure std_init
  33. *
  34. *  Standard Handler Initialization
  35. *
  36. *
  37. *       The Standard Handler estabilishes a known working environment for 
  38. *       FoxPro in the machine.  A few common variables are set up, a private
  39. *       workspace is established for the end-user, an enhanced error recovery
  40. *       handler is initialized and the BAS5 (Basic Access System version 5)
  41. *       work environment is activated.  The existance of a network is 
  42. *       determined by examining the network machine name.  A few standard
  43. *       communication windows are also established for common access use.
  44. *
  45. *
  46. *  in_lan establishes the existance of a NETBIOS machine name by examining
  47. *  SYS(0).  SET EXCLUSIVE should be controlled by this variable.  In Novell
  48. *  networks, the System Login Script should assign a machine name using the
  49. *  MACHINE="name" statement if NETBIOS is not loaded.
  50. *
  51. public in_lan, err_handl
  52. err_handl = .f.
  53. do std_sets     && added to make recreating the standard environment setting
  54.                 && accessible after modification of the operating environment
  55. in_lan =  network() .and. sys(0) # space(15)+'# 0'  && test for NETBIOS machine name
  56. if in_lan
  57.    set exclusive off
  58. else
  59.    set exclusive on
  60. endif
  61. set status off
  62. set brstatus off
  63. set deleted on
  64. *
  65. *  trap_app is used to flag when errors from APPEND BLANK are intercepted
  66. *  trap_use is used to flag when errors from USE statements are intercepted
  67. *
  68. public trap_app, trap_use
  69.  
  70. trap_app = .f.
  71. trap_use = .f.
  72. *
  73. *  err_hand is a much-enhanced error processing routine for FoxPro2
  74. *
  75. on error do err_hand with error(),message(),program(),message(1),lineno()
  76. err_handl = .t.
  77. *
  78. *  xlib is a standard error message window
  79. *  xdlg is a standard system dialog window at the screen bottom
  80. *
  81. define window xlib from 0,0 to 2,79 nofloat nogrow nozoom noclose color scheme 9
  82. define window xdlg from 22,0 to 24,79 nofloat nogrow nozoom noclose color scheme 11
  83.  
  84. activate window xdlg top
  85. @ 0,23 say 'Initializing FoxPro Environment'
  86. activate screen
  87. *
  88. *       wk_dir is a private working directory, on the drive specified by the
  89. *              DOS environment variable WKDRV
  90. *       wk_files is a guarenteed unique 8-character filename;  I usually
  91. *              reference it as (wk_dir+'\'+wk_files+'.<ext>')
  92. *       in_dir is the initial working directory
  93. *       _ret_act is a procedure to be executed on select by pickone();  it is
  94. *              a global variable (I'm lazy, and you can't necessary pick up
  95. *              other locals inside a UDF) so make sure to save the old value
  96. *              whenever you change it
  97. *       userid is a user identifier passed by the DOS environment variable
  98. *              USERID
  99. *       viewnum is used to store context switching activation, it is used by
  100. *              several routines not included in this release of my library
  101. *       help_topic specifies the active help topic for BAS5HELP
  102. public wk_dir, in_dir, wk_files, _ret_act, userid, viewnum, help_topic
  103.  
  104. viewnum = 0
  105. _ret_act = ''
  106. in_dir = sys(5) + curdir()
  107. wk_dir = getenv('WKDRV')
  108. set default to (wk_dir)
  109. wk_dir = sys(5) + curdir() + sys(3)
  110. set default to (in_dir)
  111. ! md &wk_dir >nul
  112. help_topic = 'Error Messages'
  113. if file('BAS5HELP.DBF') .and. file('BAS5HELP.FPT')
  114.    set help to BAS5HELP
  115.    set help on
  116. else
  117.    set help off
  118. endif
  119. on key label F1 do pophelp
  120. if file('BAS5KEYS.FKY')                 && BAS5KEYS sets Alt-C to Activate
  121.    restore macros from BAS5KEYS         && the calculator
  122.    on key label F2 do popcalc
  123.    on key label Shift-F2 do calc_kbd
  124. endif
  125. wk_files = sys(3)
  126.  
  127. on key label Shift-F1 do sel_pdev
  128. userid = getenv('USERID')
  129. set function 2 to ''
  130. set function 3 to ''
  131. set function 4 to ''
  132. set function 5 to ''
  133. set function 6 to ''
  134. set function 7 to ''
  135. set function 8 to chr(23)               && I use F8 rather than ^W to exit
  136. set function 9 to ''                    && from screens
  137. set function 10 to ''
  138. deactivate window xdlg
  139.  
  140. return
  141.  
  142. procedure std_sets
  143. *
  144. *  Reestablish the standard environment settings
  145. *
  146. set step off
  147. set echo off
  148. set talk off
  149. set debug off
  150. set safety off
  151. set confirm off
  152. set exact off
  153. set escape off
  154. set help off
  155. set reprocess to 10
  156. set near on
  157. *
  158. *  The following was added to allow the standard error handler to be reenabled
  159. *  by calling std_sets after having invoked it with std_init
  160. *
  161. if type('err_handl')='L'
  162.    if err_handl
  163.       on error do err_hand with error(),message(),program(),message(1),lineno()
  164.    endif
  165. endif
  166. return
  167.  
  168. procedure pophelp
  169. *
  170. *  Pophelp will activate the FoxPro context sensitive help facility if the
  171. *  Help file has been activated.
  172. *
  173. if set('HELP')='ON'
  174.    help &help_topic
  175. else 
  176.    wait 'No Help File available' window timeout 60
  177. endif
  178. return
  179.  
  180. procedure popcalc
  181. *
  182. *  Popcalc pops up the calculator if the Alt-C macro from BAS5KEYS is defined
  183. *
  184. play macro Alt_C
  185. retry
  186.  
  187. proc calc_kbd
  188. *
  189. * Calc_kbd takes the content of the calculator and places it in the current
  190. * variable or field being edited on-screen.  The code is non-intuitive;  it
  191. * checks the context of the current variable being edited to see if it is a
  192. * field or memory variable, determines the size and type of data being edited,
  193. * and then converts the calculator data to fit the field in question if it
  194. * can, or converts it to a string and keyboards the result if no better
  195. * solution can be found.
  196. *
  197. private varnm,calcstr,holdfld,fldnm
  198. calcstr = ltrim(str(_calcvalue,18,7))
  199. do while right(calcstr,1) = '0'
  200.    calcstr = left(calcstr,len(calcstr)-1)
  201. enddo
  202. varnm = varread()
  203. fldnm = alias()+'.'+varnm
  204. varnm = 'm.'+varnm
  205. clear typeahead
  206. do case
  207. case type(varnm) $ 'UDLS' .and. type(fldnm) $ 'UDL'
  208.    keyboard calcstr
  209. case type(varnm) $ 'UDLS'  && Has to be a field
  210.    do case
  211.    case type(fldnm) $ 'NF'
  212.       replace &fldnm with _calcvalue
  213.    case type(fldnm) = 'C'
  214.       replace &fldnm with calcstr
  215.    otherwise && Memo Field
  216.       keyboard calcstr
  217.    endcase
  218. case type(fldnm)$'UDL'
  219.    do case
  220.    case type(varnm) $ 'NF'
  221.       &varnm = _calcvalue
  222.    case type(varnm) = 'C'
  223.       calcstr = left(calcstr+space(len(eval(varnm))),len(eval(varnm)))
  224.       &varnm = calcstr
  225.    otherwise && Different File or illegal field type
  226.       keyboard calcstr
  227.    endcase
  228. case type(varnm)$'NF' .and. .not. type(fldnm)$'NF'
  229.    &varnm = _calcvalue
  230. case type(varnm)$'NF'
  231.    if reclock()
  232.       holdfld = eval(fldnm)
  233.       repl &fldnm with .11111111
  234.       calcstr = ltrim(str(_calcvalue,fsize(&fldnm), -int(log10(.11111111-eval(fldnm)))))
  235.       repl &fldnm with holdfld
  236.    endif
  237.    keyboard calcstr
  238. case type(fldnm)$ 'NF'
  239.    repl &fldnm with _calcvalue
  240. case type(fldnm)='M'
  241.    keyboard left(calcstr, min(len(calcstr), len(eval(varnm))))
  242. otherwise
  243.    keyboard left(calcstr, min(len(calcstr), min(len(eval(varnm)),len(eval(fldnm)))))
  244. endcase
  245. return
  246.  
  247.  
  248. procedure prg_exit
  249. *
  250. *  Exit from program and clean up temp files on work drive
  251. *
  252. parameters errmsg
  253. private erasefil
  254. set escape off
  255. on key
  256. set device to screen
  257. set console on
  258. set print off
  259. activate screen
  260. close databases
  261. flush
  262. if type('errmsg')#'C'
  263.    errmsg = ''
  264. endif
  265. do instruct with errmsg+' - returning to DOS'
  266. ?
  267. flush
  268. *
  269. *  Check to see if the temporary work directory is empty - if not, we
  270. *  must erase all files in the directory before removing it!
  271. *
  272. set default to (in_dir)
  273. erasefil = sys(2000,wk_dir+'\*.*')
  274. clear typeahead
  275. if .not. empty(erasefil)
  276.    set printer to yes.ans
  277.    ??? 'Y'+chr(13)
  278.    set printer to
  279.    flush
  280.    !erase &wk_dir <yes.ans
  281.    erase yes.ans
  282. endif
  283. ! rd &wk_dir >nul               && I _REALLY_ want to get rid of this!
  284. quit
  285.       
  286.    
  287. function fillock
  288. *
  289. *  Recoverable File lock acquisition
  290. *
  291. private setc, give_up
  292. setc = save_env()
  293. set console on
  294. set device to screen
  295. set print off
  296. if .not. flock()
  297.    give_up = .f.
  298.    activate window xlib in screen top
  299.    clear
  300.    clear typeahead
  301.    do while .not. (flock() .or. give_up)
  302.       @ 0,0 say 'Waiting for file '+trim(alias())+' - abort?' get give_up picture 'Y'
  303.       read timeout 2
  304.    enddo
  305.    clear
  306.    deactivate window xlib
  307. endif
  308. do rest_env with &setc
  309. return flock()
  310.  
  311.  
  312. function reclock
  313. *
  314. * Recoverable Single record lock acquisition
  315. *
  316. private setc, give_up
  317.  
  318. setc = save_env()
  319. set console on
  320. set device to screen
  321. set print off
  322. if .not. rlock()
  323.    give_up = .f.
  324.    activate window xlib in screen top
  325.    clear
  326.    clear typeahead
  327.    do while .not. (rlock() .or. give_up)
  328.       @ 0,0 say 'Waiting for record '+ltrim(str(recno(),9))+' in '+trim(alias())+' - abort?' get give_up picture 'Y'
  329.       read timeout 2
  330.    enddo
  331.    clear
  332.    deactivate window xlib
  333. endif
  334. do rest_env with &setc
  335. return rlock()
  336.  
  337.  
  338. PROCEDURE err_hand
  339. *
  340. *  Standard Error Processing Routine
  341. *
  342. parameters errnum, mess, curr_prg, bad_line,bad_lineno
  343. *
  344. *  errnum       FoxPro Error Message Number
  345. *  mess         FoxPro Error Description
  346. *  curr_prg     Interrupted Procedure Name
  347. *  bad_line     Interrupted Statement
  348. private setconsole,setdevice,setprint,setcolor
  349.  
  350. setconsole = sys(100)
  351. setdevice = sys(101)
  352. setprint = sys(102)
  353. setcolor = sys(2001,'COLOR')
  354. set console on
  355. set device to screen
  356. set print off
  357.  
  358. private err_screen,errlogfile
  359.  
  360. errlogfile = in_dir+sys(3)
  361. save screen to err_screen
  362.  
  363.  
  364. do case errnum
  365. case trap_app .and. errnum = 108
  366.    activate window xlib top
  367.    @ 0,0 say padc('Waiting for append in file '+trim(alias())+' - <Esc> to abort',78)
  368.    trap_app = inkey() # 27
  369.    if trap_app
  370.       retry
  371.    else
  372.       return
  373.    endif
  374.    
  375. case trap_app
  376.    trap_app = .f.
  377.    return
  378.    
  379. case trap_use .and. errnum = 108
  380.    activate window xlib top
  381.    @ 0,0 say padc('Waiting to open file '+use_alias+' - <Esc> to abort',78)
  382.    trap_use = inkey() # 27
  383.    if trap_use
  384.       retry
  385.    else
  386.       return
  387.    endif
  388.    
  389. case trap_use
  390.    trap_use = .f.
  391.    return
  392.    
  393. case errnum = 216
  394.    set console &setconsole
  395.    set device to &setdevice
  396.    set print &setprint
  397.    set color to &setcolor
  398.    return
  399.  
  400. case errnum = 4 .and. approved(.f.,'EOF limit exceeded - use EOF?',600)
  401.    go bottom
  402.    set console &setconsole
  403.    set device to &setdevice
  404.    set print &setprint
  405.    set color to &setcolor
  406.    restore screen from err_screen
  407.    if .not. bof()
  408.       retry
  409.    else
  410.       return
  411.    endif
  412.  
  413. case errnum = 1405 .and. approved(.f.,'Unable to run external program - proceed anyway?',600)
  414.    set console &setconsole
  415.    set device to &setdevice
  416.    set print &setprint
  417.    set color to &setcolor
  418.    return
  419.  
  420. case errnum = 1405 .and. approved(.f.,'Retry '+ alltrim(bad_line) + '?',600)
  421.    set console &setconsole
  422.    set device to &setdevice
  423.    set print &setprint
  424.    set color to &setcolor
  425.    retry
  426.  
  427. case errnum = 38 .and. approved(.f.,'BOF exceeded - position at BOF?',600)
  428.    go top
  429.    set console &setconsole
  430.    set device to &setdevice
  431.    set print &setprint
  432.    set color to &setcolor
  433.    restore screen from err_screen
  434.    if .not. eof()
  435.       retry
  436.    else
  437.       return
  438.    endif
  439.  
  440. case errnum = 108 .and. upper(left(alltrim(bad_line),3)) # 'USE'
  441.    if fillock()
  442.       set console &setconsole
  443.       set device to &setdevice
  444.       set print &setprint
  445.       set color to &setcolor
  446.       restore screen from err_screen
  447.       retry
  448.    else
  449.       do prg_exit with "Failed to lock file."
  450.    endif
  451.  
  452. case errnum = 108
  453.    activate window xlib
  454.    clear
  455.    @ 0,0 say 'Waiting on '+bad_line
  456.    if inkey(4) # 27
  457.       set console &setconsole
  458.       set device to &setdevice
  459.       set print &setprint
  460.       set color to &setcolor
  461.       deactivate window xlib
  462.       retry
  463.    else
  464.       do prg_exit with 'Failed to open file'
  465.    endif
  466.  
  467. case errnum = 109 .or. errnum = 130
  468.    if reclock()
  469.       set console &setconsole
  470.       set device to &setdevice
  471.       set print &setprint
  472.       set color to &setcolor
  473.       restore screen from err_screen
  474.       retry
  475.    else
  476.       do prg_exit with "Failed to lock record."
  477.    endif
  478.  
  479. case errnum = 124
  480.    set printer to
  481.    set color to &setcolor
  482.    set console &setconsole
  483.    set device to &setdevice
  484.    set print &setprint
  485.    restore screen from err_screen
  486.    return
  487.  
  488. case errnum = 125
  489.    activate window xlib
  490.    clear
  491.    @ 0,0 say 'Printer not ready.  Redirect printer output with Shift-F1'
  492.    clear typeahead
  493.    do while sys(13) = 'OFFLINE' .and. approved('Continue waiting for printer to be fixed?',2)
  494.       ?? chr(7)
  495.    enddo
  496.    deactivate window xlib
  497.    if sys(13) = 'OFFLINE'
  498.       do prg_exit with 'User aborted program.'
  499.    endif
  500.    restore screen from err_screen
  501.    set console &setconsole
  502.    set device to &setdevice
  503.    set print &setprint
  504.    set color to &setcolor
  505.    retry
  506.  
  507. otherwise
  508.    hide windows all
  509.    activate screen
  510.    @ 15,0 clear to 20,79
  511.    @ 0,0 say mess color +W/N
  512.    @ 15,0 to 20,79 double color +W/N
  513.    @ 16,8 say 'A fatal system error has occurred.  The program cannot continue.'
  514.    @ 17,8 say 'The run-time environment will be dumped to the file ERROR.LOG'
  515.    @ 18,8 say 'in the directory '+in_dir
  516.    @ 19,8 say 'Please notify Ambassador Book about this error promptly.'
  517.    set color to &setcolor
  518.    private setprtdev,disknotful
  519.    setprtdev = sys(6)
  520.    set printer to
  521.    disknotful = 'errnum # 56 .or. curdir() # in_dir'
  522.    on error set printer to 
  523.    set printer to (in_dir+'error.log') additive
  524.    on error do prg_exit with ''
  525.    set device to print
  526.    @ 1,10 say 'System error number '+ltrim(str(errnum,4))
  527.    @ 2,10 say mess
  528.    @ 4,10 say 'Called from '+curr_prg
  529.    @ 6,10 say time()
  530.    @ 6,20 say date()
  531.    @ 6,30 say 'User Name '+getenv('USERID')
  532.    @ 7,0 say '...'
  533.    @ 7,pcol()+3 say bad_lineno
  534.    @ 8,0 say bad_line
  535.    set device to screen
  536.    set console off
  537.    do instruct with 'Listing RunTime Status'
  538.    list status to print
  539.    do instruct with 'Listing Program Private Storage'
  540.    list memory to print
  541.    eject page
  542.    set console on
  543.    set printer to
  544.    if file(in_dir+'ORD.PRG')
  545.       if approved('Suspend program execution but stay in FoxBase+?')
  546.          on error
  547.          set talk on
  548.          set help on
  549.          set help to
  550.          suspend
  551.       endif
  552.    endif
  553.    do prg_exit with 'FoxBase+ runtime error occurred'
  554.  
  555. endcase
  556.  
  557. return
  558.  
  559.  
  560. procedure instruct
  561. *
  562. *  Display message on line 24
  563. *
  564. parameter msgln
  565. private setc
  566. setc = save_env()
  567. set device to screen
  568. activate screen
  569. @ 24,0 say padc(msgln,79)
  570. do rest_env with &setc
  571. return
  572.  
  573. function approved
  574. *
  575. *  Get yes or no response
  576. *
  577. parameter myanswer, question, op_dly
  578. private setc, w_name,w_pos
  579. if type('question') # 'C'
  580.    if type('question') = 'N'
  581.       op_dly = question
  582.    endif
  583.    question = myanswer
  584.    myanswer = .t.
  585. endif
  586. question = trim(question)
  587. w_name = 'A_'+alltrim(sys(2))
  588. w_pos = int((75 - len(question))/2)
  589. define window (w_name) from 21,w_pos to 23,w_pos+len(question)+4 in screen double shadow color scheme 6
  590. setc = save_env()
  591. set console on
  592. set device to screen
  593. set print off
  594. activate window (w_name) top
  595. @ 0,0 say question get myanswer picture 'Y' color scheme 8 
  596. if type('op_dly') = 'N'
  597.    read timeout op_dly
  598. else
  599.    read
  600. endif
  601. release window (w_name)
  602. do rest_env with &setc
  603. return myanswer
  604.  
  605.  
  606. function askchar
  607. *
  608. *  Get a single character response from a limited set
  609. *
  610. parameter question, valid_list
  611. private setc,myanswer
  612. setc = save_env()
  613. set console on
  614. set device to screen
  615. set print off
  616. activate window xdlg top
  617. clear
  618. myanswer = ' '
  619. @ 0,0 say trim(padc(question,78)) get myanswer picture '!' valid  myanswer $ valid_list error 'Select one of "'+valid_list+'"'
  620. read
  621. do rest_env with &setc
  622. deactivate window xdlg
  623. return myanswer
  624.  
  625. function appblank
  626. *
  627. *  Recoverable append blank
  628. *
  629. private setc
  630. setc = save_env()
  631. set console on
  632. set device to screen
  633. set print off
  634. private append_ok
  635. trap_app = .t.
  636. append blank
  637. unlock
  638. append_ok = trap_app
  639. trap_app = .f.
  640. if append_ok
  641.    append_ok = reclock()
  642. endif
  643. deactivate window xlib
  644. do rest_env with &setc
  645. return append_ok
  646.  
  647. function use_db
  648. *
  649. *  Recoverable file open
  650. *
  651. parameters dbfname, use_args
  652. private setc,use_ok
  653. setc = save_env()
  654. set console on
  655. set device to screen
  656. set print off
  657. trap_use = .t.
  658. if parameters() < 2 .or. type('use_args') # 'C'
  659.    use_args = ''
  660. endif
  661. use (dbfname) &use_args
  662. use_ok = trap_use
  663. trap_use = .f.
  664. deactivate window xlib
  665. do rest_env with &setc
  666. return use_ok 
  667.  
  668.  
  669. function save_env
  670. *
  671. *  Save standard runtime environment as a single string variable
  672. *
  673. private _rv
  674. _rv = '['+set('DEVICE')+'],['+set('CONSOLE')+'],['+set('PRINTER')+'],['
  675. _rv = _rv+set('COLOR',1)+'],['+set('ALTERNATE')+'],['+woutput()+']'
  676. return _rv
  677.  
  678.  
  679. procedure rest_env
  680. *
  681. *  Restore environment from single string expanded as parameters
  682. *
  683. parameters _dvc, _cns, _ptr, _clr, _alt, _outwin
  684. set device to &_dvc
  685. set console &_cns
  686. set printer &_ptr
  687. set color to &_clr
  688. set alternate &_alt
  689. if empty(_outwin)
  690.    activate screen
  691. else
  692.    activate window (_outwin)
  693. endif
  694. return
  695.  
  696. procedure sel_pdev
  697. *
  698. *  Sel_pdev allows the user to select an appropriate print device, change
  699. *  print modes, send control codes to the printer, make julienne fries, etc.
  700. *  Std_init sets it up to run off of <Shift>F1.  It can also be invoke by
  701. *  DO sel_pdev.  You must ensure that no pop-up array menu is active on-screen
  702. *  at the time Sel_pdev is invoked, or an error will occur and the user will
  703. *  be dropped harmlessly (well, more or less harmlessly) out to DOS.
  704. *
  705. private setc, pdev_in, pdev_wk, m_opt, p_on, spool, prnfile
  706. clear typeahead
  707. on key label Shift-F1
  708. p_on = upper(set('DEVICE')) = 'PRINT' .or. set('PRINT') = 'ON'
  709. pdev_in = set('PRINT',1)
  710. pdev_wk = left(pdev_in+space(64),64)
  711. spool = left(pdev_wk,2)='\\'
  712. prnfile = .not. (spool .or. right(trim(pdev_wk),1) = ':')
  713. m_opt = 1
  714.  
  715. dimension sel_p(4)
  716. sel_p(1) = 'Exit Print Menu'
  717. sel_p(2) = 'Print Device'
  718. sel_p(3) = 'Form Alignment'
  719. sel_p(4) = 'Network Control'
  720.  
  721. setc = save_env()
  722. set console on
  723. set device to screen
  724. set print off
  725.  
  726. define window pdev from 1,1 to 8,78 nofloat nogrow nozoom noclose title 'Print Device Assignment'
  727. activate window pdev in screen top
  728. clear
  729. activate screen
  730. do while m_opt > 0
  731.    do pdev_dsp
  732.    @ 11,1 menu sel_p, iif(in_lan,4,3) title 'Print Control'
  733.    read menu to m_opt
  734.    do case
  735.    case m_opt < 2
  736.       exit
  737.    case m_opt = 2
  738.       do set_pdev
  739.    case m_opt = 3
  740.       do mng_form
  741.    case m_opt = 4
  742.       do net_ctrl
  743.    endcase
  744.    pdev_wk = left(set('PRINT',1)+space(64),64)
  745. enddo
  746. release window pdev
  747. do rest_env with &setc
  748. on key label Shift-F1 do sel_pdev
  749. return
  750.  
  751.  
  752. procedure set_pdev
  753. private numdevs,choice, newdev
  754. numdevs = iif(in_lan,11,10)
  755. choice = iif(spool,11,iif(prnfile,10,1))
  756. dimension p_devs(11)
  757. p_devs( 1) = 'PRN:'
  758. p_devs( 2) = 'LPT1:'
  759. p_devs( 3) = 'LPT2:'
  760. p_devs( 4) = 'LPT3:'
  761. p_devs( 5) = 'COM1:'
  762. p_devs( 6) = 'COM2:'
  763. p_devs( 7) = 'COM3:'
  764. p_devs( 8) = 'AUX:'
  765. p_devs( 9) = 'NUL:'
  766. p_devs(10) = 'DISK'
  767. p_devs(11) = 'NETWORK'
  768. @ 10,40 menu p_devs,numdevs Title 'Print Devices'
  769. read menu to choice
  770. newdev = ''
  771. do case
  772. case choice = 0
  773.    return
  774. case choice < 10
  775.    newdev = p_devs(choice)
  776. case choice = 10
  777.    newdev = putfile('Print to File',iif(prnfile,trim(pdev_wk),'PRINTOUT.TXT'))
  778.    if file(newdev) .and. len(newdev)>0
  779.       if approved('File '+newdev+' exists.  Append?')
  780.          newdev = newdev + ' Additive'
  781.       else
  782.          if .not. approved(.f.,'Overwrite file '+alltrim(newdev)+'?')
  783.             newdev = ''
  784.          endif
  785.       endif
  786.    endif
  787. case choice = 11
  788.    private netp,netf,netb,netc
  789.    netp = 0
  790.    netf = 0
  791.    netb = left(getenv('USERID')+space(12),12)
  792.    netc = 1
  793.    activate window pdev in screen top
  794.    @ 5,0 clear to 5,70
  795.    @ 5,1 say 'Printer # ' get netp picture '#' range 0,4
  796.    @ 5,15 say 'Form ' get netf picture '999' range 0,255
  797.    @ 5,25 say 'Copies ' get netc picture '99' range 1,99
  798.    @ 5,40 say 'Banner (Blank = None)' get netb picture '@!'
  799.    read
  800.    @ 5,0
  801.    activate screen
  802.    newdev='\\SPOOLER\P='+str(netp,1)+'\F='+alltrim(str(netf,3))+'\C='+trim(str(netc,2))+'\'
  803.    newdev = newdev + iif(len(trim(netb))=0,'NB','B='+alltrim(netb))
  804. endcase
  805. if newdev == ''.or. upper(newdev)==upper(pdev_wk) 
  806.    return
  807. endif
  808. set printer to nul:
  809. set console off
  810. eject page
  811. set console on
  812. set printer to (newdev)
  813. if set('PRINT',1)='PRN:' .and. newdev # 'PRN:'
  814.    if prnfile
  815.       set printer to (pdev_wk) additive
  816.    else
  817.       set printer to (pdev_wk)
  818.       if .not. spool
  819.          set console off
  820.          eject page
  821.          set console on
  822.       else
  823.          _pageno = 1
  824.       endif
  825.       _plineno = 0
  826.    endif
  827. else
  828.    _pageno = 1
  829.    _plineno = 0
  830. endif
  831. return
  832.  
  833. procedure mng_form
  834. private choice,pctl_str
  835. dimension acts(4)
  836. choice = 0
  837. acts(1)='Eject to Top of Form'
  838. acts(2)='Top of Form/No Eject'
  839. acts(3)='Send Print Code'
  840. acts(4)='Change Print Status'
  841. do while .t.
  842.    activate screen
  843.    @ 10,40 menu acts,4 title 'Form Control'
  844.    read menu to choice
  845.    do case
  846.    case choice = 0
  847.       exit
  848.    case choice = 1
  849.       set console off
  850.       eject page
  851.       set console on
  852.    case choice = 2
  853.       set printer to nul:
  854.       set console off
  855.       eject page
  856.       set console on
  857.       if prnfile
  858.          set printer to (pdev_wk) additive
  859.       else
  860.          set printer to (pdev_wk)
  861.       endif
  862.    case choice = 3
  863.       activate window xdlg top
  864.       clear
  865.       pctl_str = space(150)
  866.       @ 0,0 say 'Enter String Expression:' get pctl_str picture '@S40' valid type(trim(pctl_str))='C' .or. len(alltrim(pctl_str))=0
  867.       read
  868.       pctl_str = trim(pctl_str)
  869.       if .not. (empty(pctl_str) .or. lastkey()=27)
  870.          ??? evaluate(pctl_str)
  871.       endif
  872.       deactivate window xdlg
  873.    case choice = 4
  874.       activate window pdev in screen top
  875.       @ 4,0 say 'Page: ' 
  876.       @ 4,col() get _pageno picture '9999' range 1,9999
  877.       @ 4,col()+4 say 'Line: ' 
  878.       @ 4,col() get _plineno picture '9999' range 0,_plength-1
  879.       @ 4,col()+4 say 'Column: '
  880.       @ 4,col() get _pcolno picture '999' range 0,_rmargin-1
  881.       @ 4,col()+4 say 'Lines/page: '
  882.       @ 4,col() get _plength picture '999' valid between(_plength, _plineno+1, 255)
  883.       @ 4,col()+4 say 'Cols/line: '
  884.       @ 4,col() get _rmargin picture '999' valid between(_rmargin, _pcolno+1, 255)
  885.       read
  886.       activate screen
  887.    endcase
  888. enddo
  889. return
  890.  
  891. procedure net_ctrl
  892. private choice, scr_in
  893. activate screen
  894. save screen to scr_in
  895. dimension nact(4)
  896. nact(1) = 'PCONSOLE'
  897. nact(2) = 'PRINTCON'
  898. nact(3) = 'PRINTDEF'
  899. nact(4) = 'Release Print Job'
  900. choice = 0
  901. @ 10,40 menu nact,4 title 'Network Control'
  902. read menu to choice
  903. do case
  904. case choice = 1
  905.    run /0 pconsole
  906. case choice = 2
  907.    run /0 printcon
  908. case choice = 3
  909.    run /0 printdef
  910. case choice = 4
  911.    if spool .or. .not. prnfile
  912.       set console off
  913.       set printer to nul:
  914.       eject page
  915.       set printer to (pdev_wk)
  916.       set console on
  917.    endif
  918. endcase
  919. restore screen from scr_in
  920. return
  921.  
  922. procedure pdev_dsp
  923. activate window pdev in screen top
  924. spool = left(pdev_wk,2)='\\'
  925. prnfile = .not. (spool .or. right(trim(pdev_wk),1) = ':')
  926. @ 0,0 say 'Printer Assignment: ' get pdev_wk picture '@!S45'
  927. @ 2,0 say 'Printer '+iif(p_on,'A','Ina')+'ctive   '
  928. @ 2,22
  929. do case
  930. case spool
  931.    @ 2,22 say 'SPOOLING TO NETWORK QUEUE - Spool'+ iif(_pageno+_plineno>1,' active',' empty')
  932. case prnfile
  933.    @ 2,22 say 'PRINTING TO DISK FILE'
  934. otherwise
  935.    @ 2,22 say 'PRINTER '+sys(13)
  936. endcase
  937. @ 4,0 say 'Page: ' 
  938. @ 4,col() say _pageno picture '9999'
  939. @ 4,col()+4 say 'Line: ' 
  940. @ 4,col() say _plineno picture '9999'
  941. @ 3, col()-5 say '('+str(prow(),4)+')'
  942. @ 4,col()+3 say 'Column: '
  943. @ 4,col() say _pcolno picture '999'
  944. @ 3,col()-4 say '('+str(pcol(),3)+')'
  945. @ 4,col()+3 say 'Lines/page: '
  946. @ 4,col() say _plength picture '999'
  947. @ 4,col()+4 say 'Cols/line: '
  948. @ 4,col() say _rmargin picture '999' 
  949. clear gets
  950. activate screen
  951. return
  952.  
  953. function pickone
  954. parameters disp_line,win_head,match_test,key_prefix,ul_row,ul_col,num_rows,kill_after,win_name
  955. *
  956. *  PickOne() - display and scroll through a FoxBase+ database on screen
  957. *              in a pop-up window.  Allow preconditioning of user-entered
  958. *              keys and prevent selection of any records not meeting the
  959. *              specified matching condition
  960. *
  961. *   Returns:   .T. if choice was made.  CWA positioned on selection.
  962. *              .F. if Esc was pressed.  CWA positioned as at entry.
  963. *
  964. *              Fatal errors suspend program, return .F. if resumed
  965. *
  966. *   Arguments:
  967. *              disp_line  - <ExpC>, macro of detail line content.  Must be
  968. *                           expanded to a fixed-length string
  969. *
  970. *                           Example: [LastName+" "+str(salary,9,2)+" "+title]
  971. *
  972. *              list_head  - <ExpC>, Title of pop-up box
  973. *
  974. *                           Example: [Sales Department Salaries]
  975. *
  976. *              match_test - <ExpC>, macro of selection criteria test for
  977. *                           valid, selectable records.  Must expand to a
  978. *                           logical expression. Use [.t.] for no test
  979. *
  980. *                           Example: [Department="SALES"]
  981. *
  982. *              key_prefix - <ExpC>, used as prefix on key lookup
  983. *
  984. *                           Example: []
  985. *
  986. *              ul_row     - <ExpN> Range (0..21) First box row
  987. *
  988. *                           Example: 8
  989. *
  990. *              ul_col     - <ExpN> Range (0..71) First box column
  991. *
  992. *                           Example: 4
  993. *
  994. *              num_rows   - <ExpN> Range (1..22-ul_row) Max records on
  995. *                           screen in box at one time.  If a negative
  996. *                           value is passed, a box of abs(num_rows) will
  997. *                           be displayed, but the screen will not be
  998. *                           restored on exit from the routine.
  999. *
  1000. *                            Example: 8
  1001. *
  1002. *              kill_after - <ExpN> - Maximum time to wait for a user to
  1003. *                           press a key before killing the program.  Use
  1004. *                           -1 to wait forever.
  1005. *
  1006. *                            Example: 600  (e.g. 10 minutes)
  1007. *
  1008. *              win_name   - <ExpC> - Name of window to use to display the
  1009. *                           scrolling window box (default WIN_PICK).  By
  1010. *                           specifying different window names, multiple
  1011. *                           scrolling boxes could be nested.
  1012. *
  1013. ********************************
  1014. *  Sample usage:
  1015. *
  1016. *
  1017. *   && Set up a box containing 8 records at a time anchored at 7,14.
  1018. *   && Show the user str_fld_1, date_fld_2 and num_fld_3 for each record.
  1019. *   && Label the box "This year's entries"
  1020. *   && Only allow the user undeleted records, where the date in date_fld_2
  1021. *   && falls in the current year
  1022. *   && If the user does nothing for 10 minutes, kill the program
  1023. *
  1024. *   showme = 'str_fld_1+[ ]+dtoc(date_fld_2)+[ ]+str(num_fld_3,6,2)'
  1025. *   box_title = "This year's entries"
  1026. *   must_be = '.not. deleted() .and. (year(date_fld_2) = year(date()) )'
  1027. *   top_row = 7
  1028. *   left_col = 14
  1029. *   recs_inbox = 8
  1030. *   quit_in = 600
  1031. *   
  1032. *   a= PickOne(showme,box_title,must_be,'',top_row,left_col,recs_inbox,quit_in)
  1033. ********************************
  1034. *  Validity tests and error handling:
  1035. *
  1036. *       ul_col not in range         - UDF displays error message, program
  1037. *       ul_row not in range           is SUSPENDed
  1038. *       num_rows < 1
  1039. *       type(disp_line) # 'C'
  1040. *       type(match_test) # 'L'
  1041. *       type('key_prefix') # 'C'
  1042. *       type('ul_row') # 'N'
  1043. *       type('ul_col') # 'N'
  1044. *       type('num_rows') # 'N'
  1045. *       type('kill_after') # 'N'
  1046. *
  1047. *       type('list_head') # 'C'     - Box heading is not displayed
  1048. *
  1049. *       ul_row+num_rows > 22        - num_rows reduced to (22-ul_row)
  1050. *
  1051. *       ul_col+len(&disp_line) > 78 - left(&disp_line,78-ul_col) used in
  1052. *                                     place of &disp_line
  1053. *
  1054. *       len(list_head) > boxsize    - left(list_head,boxsize) displayed
  1055. *
  1056. *       type('win_name') # 'C"      - window 'WIN_PICK' is used to display
  1057. *                                     the scrolling box.
  1058. ********************************
  1059. *
  1060. *  Gadgetry:
  1061. *
  1062. *  I write for multiuser environments, where it can be quite damaging for
  1063. *  an end-user to just walk away from a console with their program running,
  1064. *  perhaps while holding an active Flock() or Rlock(), or worse, preventing
  1065. *  someone from making a backup of the open datafiles.  I use a simple
  1066. *  inkey(1) timing loop to knock down the program after some period of 
  1067. *  inactivity in the scroll box, specified in the kill_after parameter.
  1068. *  
  1069. *  The public variable _ret_act can be used to pass the name of a DO procedure
  1070. *  to be executed on making a valid selection by pressing <Return>.  If you
  1071. *  use the pickone() routine recursively, make sure to save value of _ret_act
  1072. *  before invoking pickone() recursively.
  1073. *
  1074.  
  1075. private _up, _down, _left, _right, _bell_chr
  1076. private _pgup, _pgdn, _home, _end, _return, _esc, _f1, _f2
  1077.  
  1078. * The code makes more sense referring to a variable for keystrokes instead
  1079. * of the inkey() values or chr() arguments.
  1080.  
  1081. _up = 5
  1082. _down = 24
  1083. _left = 19
  1084. _right = 4
  1085. _bell_chr = chr(7)
  1086. _pgup = 18
  1087. _pgdn = 3
  1088. _home = 1
  1089. _end = 6
  1090. _return = 13
  1091. _esc = 27
  1092. _f1 = 28
  1093. _f2 = -1
  1094.  
  1095. private _setc
  1096.  
  1097. * _setc preserves the status on entry to the UDF
  1098.  
  1099. _setc = save_env()
  1100.  
  1101. do case                         && Test for fatal error conditions
  1102.  
  1103. case type(disp_line) # 'C'
  1104.    do psb_err with 'Display line argument must evaluate to a string',disp_line
  1105.    return .f.
  1106. case type(match_test) # 'L'
  1107.    do psb_err with 'Record match test must evaluate to a logical expression',match_line
  1108.    return .f.
  1109. case type('key_prefix') # 'C'
  1110.    do psb_err with 'Key prefix must be a character expression',key_prefix
  1111.    return .f.
  1112. case type('ul_row') # 'N'
  1113.    do psb_err with 'Upper left row argument must be numeric',ul_row
  1114.    return .f.
  1115. case type('ul_col') # 'N'
  1116.    do psb_err with 'Upper left column argument must be numeric',ul_col
  1117.    return .f.
  1118. case type('num_rows') # 'N'
  1119.    do psb_err with 'Number of rows argument must be numeric',num_rows
  1120.    return .f.
  1121. case type('kill_after') # 'N'
  1122.    do psb_err with 'Wait time between keys must be numeric',kill_after
  1123.    return .f.
  1124. case ul_col < 0 .or. ul_col > 71
  1125.    do psb_err with 'Upper left column argument out of range (0..71)',ul_col
  1126.    return .f.
  1127. case ul_row < 0 .or. ul_row > 21
  1128.    do psb_err with 'Upper left row argument out of range (0..21)',ul_row
  1129.    return .f.
  1130. endcase
  1131.  
  1132.  
  1133. private on_row,rows,boxbot,boxtop,first_col,boxsize,disp_arg,init_recno
  1134.  
  1135. * on_row is current display row
  1136. * rows is the max records that can be displayed at once
  1137. * boxbot is last display row
  1138. * boxtop is first display row
  1139. * first_col is the first column of the scrolling area
  1140. * boxsize is the width of the scroll area
  1141. * disp_arg is the content of the display line
  1142. * init_recno is the file position at entry to the routine
  1143.  
  1144. if reccount() = 0 .or. bof() .or. eof()
  1145.    return .f.
  1146. endif
  1147.  
  1148. init_recno = recno()
  1149. rows = abs(num_rows)
  1150. rows = iif(rows = 0 .or. rows+ul_row < 23, rows, 22 - ul_row)
  1151. boxbot = rows - 1
  1152. boxtop = 0
  1153. first_col = 0
  1154. disp_arg = disp_line
  1155. boxsize = max(len(evaluate(disp_arg)),7)
  1156. if ul_row + boxsize > 78
  1157.    boxsize = 78 - ul_row
  1158.    disp_arg = 'left(' + disp_line + ',' + str(boxsize,2) + ')'
  1159. endif
  1160.  
  1161. if type('win_name') # 'C'
  1162.    win_name = 'Win_Pick'
  1163. endif
  1164. if wexist(win_name)
  1165.    release window (win_name)
  1166. endif
  1167. define window (win_name) from ul_row,ul_col to ul_row + rows + 1, ul_col + boxsize + 1 double nozoom nogrow float shadow title win_head
  1168. activate window (win_name) top
  1169. on_row = boxtop
  1170. do disprecs
  1171.  
  1172. skip boxtop - on_row
  1173. on_row = boxtop
  1174. do hidisp
  1175.  
  1176. private xkey
  1177.  
  1178. * xkey is the keystroke that exits the selection loop in pickit
  1179.  
  1180. xkey = 0
  1181. do pickit
  1182. do rest_env with &_setc
  1183. if num_rows < 0
  1184.    show window (win_name) save
  1185. endif
  1186. release window (win_name)
  1187.  
  1188. if xkey = _esc 
  1189.    goto init_recno
  1190. endif
  1191.  
  1192. return (xkey # _esc)
  1193.  
  1194.  
  1195. procedure pickit
  1196. private skey, rsave, savrow, helpcnt
  1197.  
  1198. * skey is the ASCII value of the last keystroke scanned
  1199. * rsave saves the record pointer before a seek or skip action
  1200. * savrow holds the highlighted row position before PgUp or PgDn
  1201. * helpcnt works as a countdown timer for some inkey() loops
  1202.  
  1203.  
  1204. set escape off
  1205. do while .t.
  1206.    set cursor off
  1207.    helpcnt = int(kill_after)              &&  if kill_after < 0, wait forever
  1208.    xkey = 0
  1209.    do while (xkey = 0) .and. (helpcnt # 0)
  1210.       xkey = inkey(1)
  1211.       helpcnt = helpcnt - 1
  1212.       if between(helpcnt,1,45)
  1213.          activate window xlib top
  1214.          @ 0,0
  1215.          @ 0,0 say str(helpcnt,3)+' seconds left until user declared inactive!'
  1216.          ?? chr(7)
  1217.       endif
  1218.    enddo
  1219.    set cursor on
  1220.    deactivate window xlib
  1221.    if xkey = 0 .and. helpcnt = 0
  1222.       unlock all
  1223.       do prg_exit with 'Program aborted - no key pressed for '+ltrim(str(kill_after,9))+' seconds'
  1224.    endif
  1225.      skey = iif(xkey > 0,upper(chr(xkey)),'')
  1226.    do case
  1227.    case xkey = _esc
  1228.       exit
  1229.    case ( xkey = _return .and. evaluate(match_test) )
  1230.       if len(alltrim(_ret_act)) = 0
  1231.           exit
  1232.       else
  1233.       if .not. ' ' $ alltrim(_ret_act)
  1234.           do (_ret_act)
  1235.       else
  1236.           do &_ret_act
  1237.       endif
  1238.    case skey >='A' .and. skey <='Z'
  1239.       rsave = recno()
  1240.       seek key_prefix+skey
  1241.       if found()
  1242.          do disprecs
  1243.          skip boxtop - on_row
  1244.          on_row = boxtop
  1245.          do hidisp
  1246.       else
  1247.          ?? _bell_chr
  1248.          goto rsave
  1249.       endif
  1250.    case skey >='0' .and. skey <='9'
  1251.       rsave = recno()
  1252.       seek key_prefix+skey
  1253.       if found()
  1254.          do disprecs
  1255.          skip boxtop - on_row
  1256.          on_row = boxtop
  1257.          do hidisp
  1258.       else
  1259.          ?? _bell_chr
  1260.          goto rsave
  1261.       endif
  1262.  
  1263.    case xkey =  _up .and. .not. bof()
  1264.       if on_row > boxtop
  1265.          do dis
  1266.          on_row = on_row - 1
  1267.          skip -1
  1268.          do hidisp
  1269.       else
  1270.          skip -1
  1271.          if bof()
  1272.             ?? _bell_chr
  1273.             goto top
  1274.          else
  1275.             skip
  1276.             do dis
  1277.             scroll boxtop, first_col , boxbot, first_col + boxsize - 1, -1
  1278.             skip -1
  1279.             on_row = boxtop
  1280.             do hidisp
  1281.          endif
  1282.       endif
  1283.  
  1284.    case xkey =  _down .and. .not. eof()
  1285.       skip
  1286.       if eof()
  1287.          skip -1
  1288.          ?? _bell_chr
  1289.          do hidisp
  1290.       else
  1291.          skip -1
  1292.          do dis
  1293.          skip
  1294.          if on_row < boxbot
  1295.             on_row = on_row + 1
  1296.          else
  1297.             scroll boxtop, first_col , boxbot, first_col + boxsize - 1, 1
  1298.             on_row = boxbot
  1299.          endif
  1300.          do hidisp
  1301.       endif
  1302.  
  1303.    case xkey =  _pgdn .and. .not. eof()
  1304.       savrow = on_row
  1305.       skip boxbot - on_row + 1
  1306.       if eof()
  1307.          ?? _bell_chr
  1308.          go bottom
  1309.       endif
  1310.       do disprecs
  1311.       if on_row > savrow
  1312.          skip savrow - on_row
  1313.          on_row = savrow
  1314.       endif
  1315.       do hidisp
  1316.  
  1317.    case xkey =  _pgup .and. .not. bof()
  1318.       savrow = on_row
  1319.       skip boxtop - on_row - rows
  1320.       if bof()
  1321.          ?? _bell_chr
  1322.          goto top
  1323.       endif
  1324.       do disprecs
  1325.       skip savrow - on_row
  1326.       on_row = savrow
  1327.       do hidisp
  1328.  
  1329.    case xkey =  _home
  1330.       goto top
  1331.       do disprecs
  1332.       goto top
  1333.       on_row = boxtop
  1334.       do hidisp
  1335.  
  1336.    case xkey =  _end
  1337.       goto bott
  1338.       skip 1-rows
  1339.       if bof()
  1340.          go top
  1341.       endif
  1342.       do disprecs
  1343.       do hidisp
  1344.  
  1345.    case xkey = _f1
  1346.       define window wphelp from 1,1 to 20,78 shadow color scheme 10 title 'Pop-up Selection Help'
  1347.       activate window wphelp top
  1348.       clear
  1349.       ? ' This pop-up box will allow you to choose a specific item from a list of'
  1350.       ? ' potential choices by browsing through the list of choices on-screen. Lines'
  1351.       ? ' made up entirely of asterisks (*) cannot be selected from the item list.'
  1352.       ? ' You may select an item by highlighting your choice and pressing the'
  1353.       ? ' <Return> key.  Pressing the <Esc> key exits without selecting an item.'
  1354.       ?
  1355.       ? " <PgUp> and <PgDn> scroll the selection window up or down a page at a time"
  1356.       ? " "+chr(24)+" and "+chr(25)+" scroll the selection window up or down one line at a time"
  1357.       ? " <Home> and <End> move to the first and last entries respectively"
  1358.       ? " A letter (A-Z) or number (0-9) will jump to the first item starting with"
  1359.       ? " that character.  <F2> allows you to specify a search key of more than one"
  1360.       ? ' character length.  <F3> moves to the next eligible selection, while <F4>'
  1361.       ? ' moves to the previous eligible selection in the list.'
  1362.       @ 17,21 SAY "This help box will clear in    seconds"
  1363.       clear typeahead
  1364.       helpcnt = 30
  1365.       do while inkey(1) = 0 .and. helpcnt > 0
  1366.          @ 17,49 SAY helpcnt picture '99' color *N/W
  1367.          helpcnt = helpcnt - 1
  1368.       enddo
  1369.       release window wphelp
  1370.    case xkey = _f2
  1371.       activate window xdlg top
  1372.       clear
  1373.       accept 'Enter desired key--> ' to skey
  1374.       deactivate window xdlg
  1375.       rsave = recno()
  1376.       skey = key_prefix+upper(skey)
  1377.       seek skey
  1378.       do while len(skey) > len(key_prefix)+1 .and. .not. found()
  1379.          skey = left(skey,len(skey)-1)
  1380.          seek skey
  1381.       enddo
  1382.       if found()
  1383.          do disprecs
  1384.          skip boxtop - on_row
  1385.          on_row = boxtop
  1386.          do hidisp
  1387.       else
  1388.          ?? _bell_chr
  1389.          goto rsave
  1390.       endif
  1391.    case xkey = _f2 - 1 .and. .not. eof()
  1392.       rsave = recno()
  1393.       skip
  1394.       do while .not. (eof() .or. evaluate(match_test) )
  1395.          skip
  1396.       enddo
  1397.       if .not. eof()
  1398.          do disprecs
  1399.          skip boxtop - on_row
  1400.          on_row = boxtop
  1401.          do hidisp
  1402.       else
  1403.          ?? _bell_chr
  1404.          goto rsave
  1405.       endif
  1406.    case xkey = _f2 - 2 .and. .not. bof()
  1407.       rsave = recno()
  1408.       skip - 1
  1409.       do while .not. (bof() .or. evaluate(match_test) )
  1410.          skip - 1
  1411.       enddo
  1412.       if .not. bof()
  1413.          do disprecs
  1414.          skip boxtop - on_row
  1415.          on_row = boxtop
  1416.          do hidisp
  1417.       else
  1418.          ?? _bell_chr
  1419.          goto rsave
  1420.       endif
  1421.    endcase
  1422. enddo
  1423. return
  1424.  
  1425. procedure hidisp
  1426. @ on_row,0 say iif(evaluate(match_test),evaluate(disp_arg),repl('*',boxsize)) color N/W
  1427. return
  1428.  
  1429. procedure dis
  1430. @ on_row,0 say iif(evaluate(match_test),evaluate(disp_arg),repl('*',boxsize))
  1431. return
  1432.  
  1433. procedure disprecs
  1434. on_row = boxtop
  1435. clear
  1436. do while .not. eof() .and. .not. on_row = boxbot + 1
  1437.    do dis
  1438.    skip
  1439.    on_row = on_row + 1
  1440. enddo
  1441. on_row = on_row - 1
  1442. skip -1
  1443. return
  1444.  
  1445. procedure psb_err
  1446. parameters error_msg, bad_var
  1447. define window pick_psb from 9,0 to 14,79 panel title 'Window Selection Error'
  1448. activate window pick_psb top
  1449. clear
  1450. @ 0, 38 - (len(error_msg)+1)/2 say error_msg
  1451. @ 2, 31  say 'Value passed was:' color +W/N
  1452. @ 3,0 say bad_var color +W/N
  1453. wait window timeout 60
  1454. release window pick_psb
  1455. return
  1456.  
  1457. function val_crcd
  1458. *
  1459. *  Validates that the character field contains a valid credit card number
  1460. *  using modulo-10 checksumming on alternate digit multipliers
  1461. *
  1462. parameter cardno
  1463. private cksum, is_valid, cardlen, multiplier, i, factor, j
  1464. cardno = alltrim(cardno)
  1465. cardlen = len(cardno)
  1466. is_valid = inlist(cardlen,13,16)
  1467. i = cardlen
  1468. cksum = 0
  1469. multiplier = 1
  1470. do while is_valid .and. i > 0
  1471.    is_valid = isdigit(subst(cardno,i,1))
  1472.    if is_valid
  1473.       factor = alltrim(str(val(subst(cardno,i,1))*multiplier))
  1474.       for j = 1 to len(factor)
  1475.          cksum = cksum + val(subst(factor,j,1))
  1476.       endfor
  1477.    endif
  1478.    i = i - 1
  1479.    multiplier = mod(multiplier,2)+1
  1480. enddo
  1481. is_valid = is_valid .and. (mod(cksum,10) = 0)
  1482. return is_valid
  1483.  
  1484. function val_isbn
  1485. *
  1486. *  Validates that a field contains a valid ISBN
  1487. *  (International Standard Book Number) - requires a 10-character field
  1488. *
  1489. parameter test_str
  1490.  
  1491. if len(test_str) # 10
  1492.    return .f.
  1493. endif
  1494.  
  1495. if test_str = space(10)
  1496.    return .t.
  1497. endif
  1498.  
  1499. private _wk, chksum
  1500.  
  1501. chksum = at(upper(right(test_str,1)), '0123456789X') - 1
  1502. if chksum < 0
  1503.    return .f.
  1504. endif
  1505.  
  1506. _wk = ltrim(str(val(left(test_str, 9)),9))
  1507. if right(repl('0',8)+_wk,9) # left(test_str,9)
  1508.    return .f.
  1509. endif
  1510.  
  1511. _wk = _wk + ' '
  1512. do while len(_wk) > 1
  1513.    chksum = chksum + ( (asc(_wk)-48) * len(_wk))
  1514.    _wk = subst(_wk,2)
  1515. enddo
  1516. return ( mod(chksum,11) = 0)
  1517.  
  1518. procedure chg_dir
  1519. *
  1520. *  CHG_DIR()            Change directory on non-default drive
  1521. *                       and optionally set new directory as default
  1522. *
  1523. parameter new_path, set_2_dflt
  1524. if parameters() = 0
  1525.    wait window '* CHG_DIR() - no path parameter passed' timeout 60
  1526.    return .f.
  1527. endif
  1528. if type('new_path') # 'C'
  1529.    wait window '* CHG_DIR() - path parameter was not a char string' timeout 60
  1530.    return .f.
  1531. endif
  1532. if type('set_2_dflt') # 'L'
  1533.    set_2_dflt = .f.
  1534. endif
  1535. private old_drive, failed_cd
  1536. old_drive = sys(5)
  1537. failed_cd = .f.
  1538. on error failed_cd = .t.
  1539. set default to (new_path)
  1540. do std_sets
  1541. if .not. (set_2_dflt .or. failed_cd)
  1542.    set default to (old_drive)
  1543. endif
  1544. return (.not. failed_cd)
  1545.   
  1546. function isbn_ckd
  1547. parameter booknum
  1548. private chkdigs,pointer
  1549. pointer = 1
  1550. chkdigs = '0123456789X*'
  1551. do while .not. val_isbn(booknum+subst(chkdigs,pointer,1)
  1552.    pointer = pointer + 1
  1553.    if pointer > 11
  1554.       exit
  1555.    endif
  1556. enddo
  1557. return subst(chkdigs,pointer,1)